﻿using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;

namespace System. Architecture
{
    public interface IMVVMContentLoader
    {
        object GetView(string Url, bool attachViewModel);
        void AttachViewModel(object view, object viewModel);
        object FindOutViewModel(string token);

        object GetView(Func<ExportFactory<IView, ExportViewAttribute>, bool> ViewFactoryPredicate, bool attachViewModel);
        object GetView(ExportFactory<IView, ExportViewAttribute> factory, bool attachViewModel);
        IEnumerable<ExportFactory<IView, ExportViewAttribute>> GetViewFactories(Func<ExportFactory<IView, ExportViewAttribute>, bool> ViewFactoryPredicate);
    }

    [Shared]
    [Export(typeof(IMVVMContentLoader))]
    public class MMVMContentLoader : IMVVMContentLoader
    {
        private readonly IEnumerable<ExportFactory<IView, ExportViewAttribute>> viewFactories;
        private readonly IEnumerable<ExportFactory<IViewModel, ExportViewModelAttribute>> viewModelFactories;


        [ImportingConstructor]
        public MMVMContentLoader(
            [ImportMany]
            IEnumerable<ExportFactory<IView, ExportViewAttribute>> viewFactories,
            [ImportMany]
            IEnumerable<ExportFactory<IViewModel, ExportViewModelAttribute>> viewModelFactories)
        {
            this.viewFactories = viewFactories;
            this.viewModelFactories = viewModelFactories;
        }

        public IEnumerable<ExportFactory<IView, ExportViewAttribute>> GetViewFactories(Func<ExportFactory<IView, ExportViewAttribute>, bool> ViewFactoryPredicate)
        {
            return viewFactories.Where(ViewFactoryPredicate);
        }

        public virtual object GetView(ExportFactory<IView, ExportViewAttribute> factory, bool attachViewModel)
        {
            // If no suitable page is found then throw an exception
            if (factory == null)
                throw new InvalidOperationException("pageFactory is null");

            // Create the page instance
            Export<IView> pageLifetimeContext = factory.CreateExport();
            ExportViewAttribute viewMetadata = factory.Metadata;
            object view = pageLifetimeContext.Value;

            var viewModel = FindOutViewModel(viewMetadata.CorrelationToken);

            if (attachViewModel && viewModel != null)
                AttachViewModel(view, viewModel);

            return view;
        }

        public virtual object GetView(Func<ExportFactory<IView, ExportViewAttribute>, bool> ViewFactoryPredicate, bool attachViewModel)
        {
            ExportFactory<IView, ExportViewAttribute> pageFactory = viewFactories.FirstOrDefault(ViewFactoryPredicate);

            return GetView(pageFactory, attachViewModel);
        }

        public virtual object GetView(string Url, bool attachViewModel)
        {
            return GetView((ef) => object.Equals(ef.Metadata.Url, Url), attachViewModel);
        }


        public virtual void AttachViewModel(object view, object viewModel)
        {
            if (view is FrameworkElement)
                ((FrameworkElement)view).DataContext = viewModel;
        }
        
        public virtual object FindOutViewModel(string token)
        {
            // Attach the view model (if one exists)
            ExportFactory<IViewModel, ExportViewModelAttribute> viewModelFactory = viewModelFactories.Where((export) =>
                {
                    var t = export.Metadata.CorrelationToken == token;
                    return t;
                }
                    ).FirstOrDefault();
            Export<IViewModel> viewModelLifetimeContext = null;

            if (viewModelFactory != null)
            {
                // Create the view model instance
                viewModelLifetimeContext = viewModelFactory.CreateExport();

                object viewModel = viewModelLifetimeContext.Value;

                return viewModel;
            }

            return null;
        }
    }
}
